package com.passion.mq.config;

import lombok.Data;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Declarable;
import org.springframework.amqp.core.Declarables;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类描述：rabbitmq配置类
 *
 * @author ZhuYin
 * @since 2024年12月28日
 */
@Data
@Configuration
@ConditionalOnProperty(prefix = "archives.mq", name = "type", havingValue = "rabbitmq")
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitMqConfig {

    private List<QueueConfig> queues;
    private List<ExchangeConfig> exchanges;
    private List<BindingConfig> bindings;

    @Resource
    private ApplicationContext applicationContext;

    /**
     * 用于存储已经创建的队列和交换机
     */
    private final Map<String, Queue> queueMap = new HashMap<>();
    private final Map<String, Exchange> exchangeMap = new HashMap<>();

    @Bean
    public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public Declarables declareQueues() {
        // 创建队列并将它们存储在 queueMap 中
        queues.forEach(queueConfig -> {
            Queue queue = createQueue(queueConfig);
            queueMap.put(queueConfig.getName(), queue);
        });
        return new Declarables(queueMap.values().toArray(new Declarable[0]));
    }

    @Bean
    public Declarables declareExchanges() {
        // 创建交换机并将它们存储在 exchangeMap 中
        exchanges.forEach(exchangeConfig -> {
            Exchange exchange = createExchange(exchangeConfig);
            exchangeMap.put(exchangeConfig.getName(), exchange);
        });
        return new Declarables(exchangeMap.values().toArray(new Declarable[0]));
    }

    @Bean
    public Declarables declareBindings() {
        // 创建绑定
        return new Declarables(bindings.stream()
                .map(this::createBinding)
                .toArray(Declarable[]::new));
    }

    private Queue createQueue(QueueConfig queueConfig) {
        Map<String, Object> arguments = queueConfig.getArguments();
        for (Map.Entry<String, Object> entry : arguments.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            // 特殊参数处理，否则会报错
            String integerKey = "x-message-ttl";
            if (key.equalsIgnoreCase(integerKey)) {
                // 该参数的配置值要转成整数
                arguments.put(key, Integer.parseInt(value + ""));
            }
        }
        return QueueBuilder.durable(queueConfig.getName())
                .withArguments(arguments)
                .build();
    }

    private Exchange createExchange(ExchangeConfig exchangeConfig) {
        switch (exchangeConfig.getType()) {
            case "direct":
                return ExchangeBuilder.directExchange(exchangeConfig.getName())
                        .durable(exchangeConfig.isDurable())
                        .withArguments(exchangeConfig.getArguments())
                        .build();
            case "topic":
                return ExchangeBuilder.topicExchange(exchangeConfig.getName())
                        .durable(exchangeConfig.isDurable())
                        .withArguments(exchangeConfig.getArguments())
                        .build();
            case "fanout":
                return ExchangeBuilder.fanoutExchange(exchangeConfig.getName())
                        .durable(exchangeConfig.isDurable())
                        .withArguments(exchangeConfig.getArguments())
                        .build();
            case "headers":
                return ExchangeBuilder.headersExchange(exchangeConfig.getName())
                        .durable(exchangeConfig.isDurable())
                        .withArguments(exchangeConfig.getArguments())
                        .build();
            default:
                throw new IllegalArgumentException("Unsupported exchange type: " + exchangeConfig.getType());
        }
    }

    private Binding createBinding(BindingConfig bindingConfig) {
        // 从 queueMap 和 exchangeMap 中获取已经创建的队列和交换机
        Queue queue = queueMap.get(bindingConfig.getQueue());
        if (queue == null) {
            throw new IllegalArgumentException("Queue not found: " + bindingConfig.getQueue());
        }

        Exchange exchange = exchangeMap.get(bindingConfig.getExchange());
        if (exchange == null) {
            throw new IllegalArgumentException("Exchange not found: " + bindingConfig.getExchange());
        }

        // 创建绑定
        return BindingBuilder.bind(queue)
                .to(exchange)
                .with(bindingConfig.getRoutingKey())
                .noargs();
    }

    @Data
    public static class QueueConfig {
        private String name;
        private boolean durable = true;
        private boolean autoDelete = false;
        private Map<String, Object> arguments = new HashMap<>();
        private List<ConsumerConfig> consumer;
    }

    @Data
    public static class ConsumerConfig {
        private Integer concurrency;
    }

    @Data
    public static class ExchangeConfig {
        private String name;
        private String type;
        private boolean durable = true;
        private boolean autoDelete = false;
        private Map<String, Object> arguments = new HashMap<>();
    }

    @Data
    public static class BindingConfig {
        private String queue;
        private String exchange;
        private String routingKey;
    }
}